/*
 * ProfileGenerator.cpp
 *
 *  Created on: 8 gru 2013
 *      Author: malwina.kowalczyk
 */

#include "ProfileGenerator.hpp"
#include <algorithm>
 #include <iostream>

generator::ProfileGenerator::ProfileGenerator()
{
}

/*
* Generates cartesian product of  n sets, where n is number of markers.
* One set consists of tuples(pairs/triples) of genotypes generated within one marker 
*/
bool generator::ProfileGenerator::generateAll(
	const std::list<std::list<dna::genotypeSet>>& markersPossibleGenotypeSets, 
	outputPtr resultOutput,int howManyPeople)
{
	// size_t numberOfMarkers = markersPossibleGenotypeSets.size();

	std::vector<std::list<dna::genotypeSet>::const_iterator> indexes;
	std::vector<std::list<dna::genotypeSet>::const_iterator> end_of_lists;
	std::vector<std::list<dna::genotypeSet>::const_iterator> begins_of_lists;

	std::list<std::list<dna::genotypeSet>>::const_iterator markersIterator;
	int i = 0;
	for (markersIterator = markersPossibleGenotypeSets.begin(); markersIterator != markersPossibleGenotypeSets.end(); ++markersIterator) {
	    indexes.push_back(markersIterator->begin());
	    begins_of_lists.push_back(markersIterator->begin());
	    end_of_lists.push_back(markersIterator->end());
	    i++;
	}
	bool continue_generation = true;
	while (continue_generation) {
		resultOutput->put(getProfileTupleForCurrentIndex(markersPossibleGenotypeSets, indexes, howManyPeople));
		continue_generation = increaseIndex(indexes, end_of_lists, begins_of_lists);
	}
	return true;
}

/*
* Extracts next product of sets from markersPossibleGenotypeSets based on current index
* where 0 <= index[i]  < (i'th marker genotypes count) and index[i] indicates which tuple from i'th marker genotypes should be taken
 */
std::vector<dna::profile> generator::ProfileGenerator::getProfileTupleForCurrentIndex(
	const std::list<std::list<dna::genotypeSet>>& markersPossibleGenotypeSets, 
	const std::vector<std::list<dna::profile>::const_iterator>& indexes,
	int howManyPeople)
{
	
	std::vector<dna::profile> resultTuple(howManyPeople);

	for (std::size_t i=0;  i< indexes.size(); i++) {
		dna::genotypeSet selectedSet = *indexes[i];

		dna::genotypeSet::const_iterator setIterator;
		std::vector<dna::profile>::iterator resultTupleIterator;
		resultTupleIterator = resultTuple.begin();
		for (setIterator = selectedSet.begin(); setIterator!=selectedSet.end(); ++setIterator)
		{
			dna::genotype para = std::make_pair(setIterator->first, setIterator->second);
			resultTupleIterator->push_back(para);
			++resultTupleIterator;
		}
	}
	return resultTuple;
}

bool generator::ProfileGenerator::increaseIndex(
	std::vector<std::list<dna::profile>::const_iterator>& indexes, 
	const std::vector<std::list<dna::profile>::const_iterator>& end_of_lists, 
	const std::vector<std::list<dna::profile>::const_iterator>& begins_of_lists) 
{
	size_t numerOfMarkers = indexes.size();
	size_t i = 0;
	while (i <numerOfMarkers) {
		++indexes[i];
		if (indexes[i] == end_of_lists[i]) {
			indexes[i] = begins_of_lists[i];
			i++;
		} else {
			break;
		}
	}
	return i!=numerOfMarkers;
}
